03 宏观认识:整体架构

工欲善其事,必先利其器。本节我们来从宏观上认识下 K8S 的整体架构,以便于后续在此基础上进行探索和实践。

C/S 架构

从更高层来看,K8S 整体上遵循 C/S 架构,从这个角度来看,可用下面的图来表示其结构:

                               +-------------+                              
                               |             |                              
                               |             |               +---------------+
                               |             |       +-----> |     Node 1    |
                               | Kubernetes  |       |       +---------------+
+-----------------+            |   Server    |       |                      
|       CLI       |            |             |       |       +---------------+
|    (Kubectl)    |----------->| ( Master )  |<------+-----> |     Node 2    |
|                 |            |             |       |       +---------------+
+-----------------+            |             |       |       
                               |             |       |       +---------------+
                               |             |       +-----> |     Node 3    |
                               |             |               +---------------+
                               +-------------+               

左侧是一个官方提供的名为 kubectl 的 CLI (Command Line Interface)工具,用于使用 K8S 开放的 API 来管理集群和操作对象等。

右侧则是 K8S 集群的后端服务及开放出的 API 等。根据上一节的内容,我们知道 Node 是用于工作的机器,而 Master 是一种角色(Role),表示在这个 Node 上包含着管理集群的一些必要组件。具体组件的详细介绍参考第 11 小节对各组件的详细剖析。

当然在这里,只画出了一个 Master,在生产环境中,为了保障集群的高可用,我们通常会部署多个 Master 。

Master

下面我们来逐层分解, 首先是 Master ,这里我们只介绍其管理集群的相关组件。Master 是整个 K8S 集群的“大脑”,与大脑类似,它有几个重要的功能:

  • 接收:外部的请求和集群内部的通知反馈
  • 发布:对集群整体的调度和管理
  • 存储:存储

这些功能,也通过一些组件来共同完成,通常情况下,我们将其称为 control plane 。如下图所示:

+----------------------------------------------------------+          
| Master                                                   |          
|              +-------------------------+                 |          
|     +------->|        API Server       |<--------+       |          
|     |        |                         |         |       |          
|     v        +-------------------------+         v       |          
|   +----------------+     ^      +--------------------+   |          
|   |                |     |      |                    |   |          
|   |   Scheduler    |     |      | Controller Manager |   |          
|   |                |     |      |                    |   |          
|   +----------------+     v      +--------------------+   |          
| +------------------------------------------------------+ |          
| |                                                      | |          
| |                Cluster state store                   | |          
| |                                                      | |          
| +------------------------------------------------------+ |          
+----------------------------------------------------------+          

它主要包含以下几个重要的组成部分。

Cluster state store

存储集群所有需持久化的状态,并且提供 watch 的功能支持,可以快速的通知各组件的变更等操作。

因为目前 Kubernetes 的存储层选择是 etcd ,所以一般情况下,大家都直接以 etcd 来代表集群状态存储服务。即:将所有状态存储到 etcd 实例中。

刚才我们说 Master 相当于是 K8S 集群的大脑,更细化来看,etcd 则是大脑中的核心,为什么这么说?可以参考后面详细剖析的章节,本章我们先从更高的层次来看集群的整体架构。

你可能会问, etcd 是必须的吗?就目前而言,etcd 是必须的,这主要是 Kubernetes 的内部实现。

而早在 2014 年左右,社区就一直在提议将存储层抽象出来,后端的实际存储作为一种插件化的存在。呼声比较大的是另一种提供 k/v 存储功能的 Consul

不过得益于 etcd 的开发团队较为活跃,而且根据 K8S 社区的反馈做了相当大的改进,并且当时 K8S 团队主要的关注点也不在此,所以直到现在 etcd 仍不是一个可选项。

如果现在去看下 Kubernetes 的源代码,你会发现存储层的代码还比较简洁清晰,后续如果有精力也许将此处插件化也不是不可能。

API Server

这是整个集群的入口,类似于人体的感官,接收外部的信号和请求,并将一些信息写入到 etcd 中。

实际处理逻辑比三次握手简单的多:

  • 请求 API Server :“嗨,我有些东西要放到 etcd 里面”
  • API Server 收到请求:“你是谁?我为啥要听你的”
  • 从请求中,拿出自己的身份凭证(一般是证书):“是我啊,你的master,给我把这些东西放进去”
  • 这时候就要看是些什么内容了,如果这些内容 API Server 能理解,那就放入 etcd 中 “好的 master 我放进去了”;如果不能理解,“抱歉 master 我理解不了”

可以看到,它提供了认证相关的功能,用于判断是否有权限进行操作。当然 API Server 支持多种认证方法,不过一般情况下,我们都使用 x509 证书进行认证。

API Server 的目标是成为一个极简的 server,只提供 REST 操作,更新 etcd ,并充当着集群的网关。至于其他的业务逻辑之类的,通过插件或者在其他组件中完成。关于这部分的详细实现,可以参考后面的 API Server 剖析相关章节。

Controller Manager

Controller Manager 大概是 K8S 集群中最繁忙的部分,它在后台运行着许多不同的控制器进程,用来调节集群的状态。

当集群的配置发生变更,控制器就会朝着预期的状态开始工作。

Scheduler

顾名思义,Scheduler 是集群的调度器,它会持续的关注集群中未被调度的 Pod ,并根据各种条件,比如资源的可用性,节点的亲和性或者其他的一些限制条件,通过绑定的 API 将 Pod 调度/绑定到 Node 上。

在这个过程中,调度程序一般只考虑调度开始时, Node 的状态,而不考虑在调度过程中 Node 的状态变化 (比如节点亲和性等,截至到目前 v1.11.2 也暂未加入相关功能的稳定特性)

Node

Node 的概念我们在上节已经提过了,这里不再过多赘述,简单点理解为加入集群中的机器即可。

那 Node 是如何加入集群接受调度,并运行服务的呢?这都要归功于运行在 Node 上的几个核心组件。我们先来看下整体结构:

+--------------------------------------------------------+       
| +---------------------+        +---------------------+ |       
| |      kubelet        |        |     kube-proxy      | |       
| |                     |        |                     | |       
| +---------------------+        +---------------------+ |       
| +----------------------------------------------------+ |       
| | Container Runtime (Docker)                         | |       
| | +---------------------+    +---------------------+ | |       
| | |Pod                  |    |Pod                  | | |       
| | | +-----+ +-----+     |    |+-----++-----++-----+| | |       
| | | |C1   | |C2   |     |    ||C1   ||C2   ||C3   || | |       
| | | |     | |     |     |    ||     ||     ||     || | |       
| | | +-----+ +-----+     |    |+-----++-----++-----+| | |       
| | +---------------------+    +---------------------+ | |       
| +----------------------------------------------------+ |       
+--------------------------------------------------------+  

Kubelet

Kubelet 实现了集群中最重要的关于 Node 和 Pod 的控制功能,如果没有 Kubelet 的存在,那 Kubernetes 很可能就只是一个纯粹的通过 API Server CRUD 的应用程序。

K8S 原生的执行模式是操作应用程序的容器,而不像传统模式那样,直接操作某个包或者是操作某个进程。基于这种模式,可以让应用程序之间相互隔离,互不影响。此外,由于是操作容器,所以应用程序可以说和主机也是相互隔离的,毕竟它不依赖于主机,在任何的容器运行时(比如 Docker)上都可以部署和运行。

我们在上节介绍过 Pod,Pod 可以是一组容器(也可以包含存储卷),K8S 将 Pod 作为可调度的基本单位, 分离开了构建时和部署时的关注点:

  • 构建时,重点关注某个容器是否能正确构建,如何快速构建
  • 部署时,关心某个应用程序的服务是否可用,是否符合预期,依赖的相关资源是否都能访问到

这种隔离的模式,可以很方便的将应用程序与底层的基础设施解耦,极大的提高集群扩/缩容,迁移的灵活性。

在前面,我们提到了 Master 节点的 Scheduler 组件,它会调度未绑定的 Pod 到符合条件的 Node 上,而至于最终该 Pod 是否能运行于 Node 上,则是由 Kubelet 来裁定的。关于 Kubelet 的具体原理,后面有详细剖析的章节。

Container runtime

容器运行时最主要的功能是下载镜像和运行容器,我们最常见的实现可能是 Docker , 目前还有其他的一些实现,比如 rkt, cri-o

K8S 提供了一套通用的容器运行时接口 CRI (Container Runtime Interface), 凡是符合这套标准的容器运行时实现,均可在 K8S 上使用。

Kube Proxy

我们都知道,想要访问某个服务,那要么通过域名,要么通过 IP。而每个 Pod 在创建后都会有一个虚拟 IP,K8S 中有一个抽象的概念,叫做 Servicekube-proxy 便是提供一种代理的服务,让你可以通过 Service 访问到 Pod。

实际的工作原理是在每个 Node 上启动一个 kube-proxy 的进程,通过编排 iptables 规则来达到此效果。深入的解析,在后面有对应的章节。

总结

本节中,我们了解到了 K8S 的整体遵循 C/S 架构,集群的 Master 包含着几个重要的组成部分,比如 API Server, Controller Manager 等。

而 Node 上,则运行着三个必要的组件 kubelet, container runtime (一般是 Docker), kube-proxy

通过所有组件的分工协作,最终实现了 K8S 对容器的编排和调度。

完成了这节的学习,那我们就开始着手搭建一个属于我们自己的集群吧。